﻿using System;
using System.Collections.Generic;
using Moq;
using NUnit.Framework;

namespace Devme.Diagnostics.Tests
{
    [TestFixture]
    public class StopwatchManagerTest
    {
        [SetUp]
        public void Setup()
        {
            var factoryMock = new Mock<IStopwatchFactory>();

            var times = new Queue<TimeSpan>();

            // random delays
            times.Enqueue(TimeSpan.FromSeconds(123));
            times.Enqueue(TimeSpan.FromSeconds(56));
            times.Enqueue(TimeSpan.FromSeconds(95));
            times.Enqueue(TimeSpan.FromSeconds(11));
            times.Enqueue(TimeSpan.FromSeconds(678));
            times.Enqueue(TimeSpan.FromSeconds(42));
            times.Enqueue(TimeSpan.FromSeconds(3));
			times.Enqueue(TimeSpan.FromSeconds(58));

            factoryMock
                .Setup(x => x.Create())
                .Returns(() =>
                {
                    TimeSpan elapsed = times.Dequeue();
                    var stopwatchMock = new Mock<IStopwatch>();
                    stopwatchMock
                        .Setup(x => x.Elapsed)
                        .Returns(elapsed);
                    return stopwatchMock.Object;
                });

            StopwatchManager.Factory = factoryMock.Object;
        }

        [Test]
        public void ShouldReturnActualExecutionTimeOfSingleBlock()
        {
            TimeSpan expected = TimeSpan.FromSeconds(123);
            TimeSpan actual = TimeSpan.Zero;
            using (StopwatchManager.Start(result => { actual = result; }))
            {
            }
            Assert.AreEqual(expected, actual);
        }

        [Test]
        public void ShouldReturnActualExecutionTimeOfSeveralBlockAsSingleUnit()
        {
            StopwatchResult expected1 = new StopwatchResult(TimeSpan.FromSeconds(58), TimeSpan.FromSeconds(123 + 56 + 42 + 58), 4, TimeSpan.FromSeconds(42), TimeSpan.FromSeconds(123));
            StopwatchResult expected2 = new StopwatchResult(TimeSpan.FromSeconds(678), TimeSpan.FromSeconds(11 + 678), 2, TimeSpan.FromSeconds(11), TimeSpan.FromSeconds(678));
            StopwatchResult expected3 = new StopwatchResult(TimeSpan.FromSeconds(95), TimeSpan.FromSeconds(95), 1, TimeSpan.FromSeconds(95), TimeSpan.FromSeconds(95));
            StopwatchResult expected4 = new StopwatchResult(TimeSpan.FromSeconds(3), TimeSpan.FromSeconds(3), 1, TimeSpan.FromSeconds(3), TimeSpan.FromSeconds(3));

            StopwatchResult actual1 = null;
            StopwatchResult actual2 = null;
            StopwatchResult actual3 = null;
            StopwatchResult actual4 = null;
            using (StopwatchManager.Start("block1", result => { actual1 = result; }))
            {
            }
            using (StopwatchManager.Start("block1", result => { actual1 = result; }))
            {
            }
            using (StopwatchManager.Start("block3", result => { actual3 = result; }))
            {
            }
            using (StopwatchManager.Start("block2", result => { actual2 = result; }))
            {
            }
            using (StopwatchManager.Start("block2", result => { actual2 = result; }))
            {
            }
            using (StopwatchManager.Start("block1", result => { actual1 = result; }))
            {
            }
            using (StopwatchManager.Start("block4", result => { actual4 = result; }))
            {
            }
			using (StopwatchManager.Start("block1", result => { actual1 = result; }))
			{
			}

            Assert.AreEqual(expected1, actual1);
            Assert.AreEqual(expected2, actual2);
            Assert.AreEqual(expected3, actual3);
            Assert.AreEqual(expected4, actual4);
        }
    }
}
